XT: 8086 дизасемблер
Передмова
У попередній статті про InfoSec редактор BE ми розглянули просте завдання написати термінальний дизасемблер використовуючи існуючі бібліотеки дизасемблювання. У цій статті ми покажемо як написати таку бібліотеку самим.
Ця вправа може стати лабораторною роботою, або курсовою роботою по курсу "КВ-123-17: Мікропроцесорні архітектури" для студентів першого курсу, які тільки знайомляться з існуючими архітектурами. Зазвичай у педагогічному процесі вибираються прості мікропроцесорні архітектури (такі як MIPS) або спеціалізовані для викладання (такі як RISC-V). Так чи інакше, працюючи у цій області, вам доведеться зіткнутися зі всіма архітектурами.
У якості асемблера, або мови програмування на якій пропонується виконувати завдання ми вибрали NASM, який вже використали для побудови термінального дизасемблера. Заодно буде з чим порівнювати, так як ndisasm з поставки nasm займає у бінарному вигляді 1.5 мегабайта. Ми ж даємо обмеження у цьому завданні на дизасемблер — 64 кілобайта.
Щоб вернути x86 архітектуру в академічне русло, ми обмежемо набір інструкцій системою команд мікропроцесора 8086, яка розширяє систему команд 8-бітних процесорів 8085 за допомогою додаткового байта MOD_R/M і збільшує таким чином розмір команди до 6 байт (довжина конвеєру).
Вступ
У курсі "КВ-123-16 Основи схемотехніки" для студентів першого курсу ви знайомитеся з чого складається елементна база 2-бітних мікропроцесорів Intel 3000, та як збирати такі системи самому. У якості підручника ми використовуємо "Цифрові ЕОМ: Практикум". Самофалов, Корнійчук, Тарасенко, Жабін. Київ, "Вища школа", 1990. Однак після 2-бітних, де ви самі програмували матрицю мікрокоду для команд, 4-бітних та 8-бітних процесорів, тільки 16-бітні процесори відкривають нову епоху сучасних процесорів Intel. Починаючи з цієї архітектури далі усі подальші процесори компанії Intel були сумісними між собою, тож вивчивши архітектуру 8086 ви зрозумієте контекст розвитку EM64T.
Рік | Код | Бітність | Опис | Чіпсет |
---|---|---|---|---|
1970 | 3000 | 2 | Intel 3000 | DIY |
1971 | 4000 | 4 | Intel 4000 | MCS-4 |
1974 | ZX | 8 | Intel 8085/8080 | MCS-85 |
1976 | XT | 16 | Intel 8086/8088 | MCS-86 |
1982 | AT | 16 | Intel 80286 | PS/2 Model 30 |
1985 | IA-32 | 32 | Intel 80386 | PS/2 Model 50 |
1989 | IA-32 | 32 | Intel 80486 | PS/2 Model 70 |
1993 | IA-32 | 32 | Intel Pentium (P5/P54C) | Triton |
1995 | IA-32 | 32 | Intel Pentium Pro/II/III | 82443BX |
2000 | EM64T | 64 | Intel Pentium 4 | 82865P |
2006 | EM64T | 64 | Intel Core | Q35 |
2008 | EM64T | 64 | Intel Atom | |
2011 | AVX | 128 | Intel Core (Sandy Bridge) | Z77, X99 |
2013 | AVX-2 | 256 | Intel Core (Haswell) | Z97 |
2017 | AVX-512 | 512 | Intel Phi, Core (Skylake) | Z370, X299 |
2023 | AMX | 1024 | Intel Core (Sapphire Rapids) | W790 |
Мотивація
Особливо важливий це курс для тих, хто на третьому році навчання буде слухати курси на кафедрі мовного забезпечення КА-121, такі як "КА-121-04 Верифікація програмного забезпечення", "КА-121-03 Верифікація мікропроцесорних архітектур", та "КА-121-05 Верифікація об`єктного коду".
Корисним цей курс буде для всіх, хто хоче навчитися вивчити асемблер у повному обсязі, так як ми пишемо асемблер та/або дизасеблер. У залежності від технічної орієнтації студентів, можна взяти альтернативний курс розробки LISP інтерпретатора на асемблері, але це вимагає знання програми другого року навчання.
Анотація
TL;DR — Дизасемблер на асемблері для Mac за 1 день на 672 байта для 45 опкодів, де використовується байт MOD_R/M (00, 01, 02, 03, 08, 09, 0A, 0B, 10, 11, 12, 13, 18, 19, 1A, 1B, 20, 21, 22, 23, 29, 29, 2A, 2B, 30, 31, 32, 33, 3C, 3D, 3E, 3F, 88, 89, 8A, 8B, 8D, 80, 81, 84, 85, 86, 87, C4, C5), тобто одразу для половини найскладніших мнемонік. Інші мнемоніки простіші та нагадуються 8085 асемблер.
Специфікація на архітектуру опкодів
Система команд 8086 розділяється на наступні категорії (загалом 89 мнемонік + 4 сегментних префікси): 1) інструкції пересилки даних (14): MOV, PUSH, POP, XCHG, IN, OUT, XLAT, LEA, LDS, LES, LAHF, SAHF, PUSHF, POPF; 2) арифметичні інструкції (20): ADD, ADC, INC, AAA, BAA, SUB, SSB, DEC, NEG, CMP, AAS, DAS, MUL, IMUL, AAM, DIV, IDIV, AAD, CBW, CWD; 3) логічні інструкції (11): NOT, SHL, SHR, ROL, ROR, RCL, RCR, AND, TEST, OR, XOR; 4) маніпуляція з послідовностями (6): REP, MOVS, CMPS, SCAS, LODS, STOS; 5) управління потоком виконання (27): CALL, JMP, RET, INT, INT 3, INTO, IRET, JE, LJ, JLE, JB, JBE, JP, JO, JS, JNE, JNL, JNLE, JNB, JUNBE, JNP, JNO, JNS, LOOP, LOOPZ, LOOPNZ, JCXZ; 6) управління станом процесору (11): CLC, CMC, STC, CLD, STD, CLI, STI, HLT, WAIT, ESC, LOCK.
Тут ми використовуємо виключно синтаксичну специфікацію, тобто визначення на формат бінарної серіалізації певної програми певної архітектури. На курсі формальної верифікації будуть використовуватися семантичні формальні моделі для кожної інструкції які будуть у сутності формальними програмами на MLTT які відповідають псевдокоду для кожної інструкції з документації Intel SDM. В цих специфікацях детально буде показано які біти яких регістрів змінюються та що відбувається з шиною пам'яті та даних. Зараз же ми соредимося виключно на серіалазації та компактифікації дизасемблера, тобто на синтаксичній специфікації.
Легенда
v 0: count=1; 1: count=CL;
d 0: from reg; 1: to reg;
w 0: byte; 1: word;
sg 00: ES; 01: CS; 10: SS; 11: DS;
mod 00: disp=0, disp-lo=N/A, disp-hi=N/A;
01: disp=disp-lo sign-extended to 16-bit, disp-hi=N/A;
10: disp=[disp-hi,disp-lo];
11: r/m treaded as reg field;
reg 000: AX AL; 001: CX CL; 010: DX DL; 011: BX BL;
100: SP AH; 101: BP CH; 110: SI DH; 111: DI BH;
r/m 000: (BX) + (SI) + disp; 001: (BX) + (DI) + disp;
010: (BP) + (SI) + disp; 011: (BP) + (DI) + disp;
100: (SI) + disp; 101: (DI) + disp;
110: (BP) + disp or direct address when mod=00; 111: (BX) + disp;
Загальний формат інструкції
LOCK | префікс блокування шини
REP | префікс циклічних операцій
SEG | префікс перевизначення сегменту
OPCODE | головний байт по якому здійснюється діспатч
MOD R/M | байт mod r/m для кодування адресації та порядку операндів
DISP | відносна адреса на шині
IMM | безпосередній операнд
Таблиця мнемонік
Таблиця мнемонік буде вміщувати 136 рядків, однак ми звузимо завдання до лобораторної роботи для для першого рядка, це одразу 4 варіанта інструкції ADD.
1:
ADD | 1000 00sw | mod 000 r/m | data | data if sw=01
ADC | 1000 00sw | mod 010 r/m | data | data if sw=01
SBB | 1000 00sw | mod 011 r/m | data | data if sw=01
SUB | 1000 00sw | mod 101 r/m | data | data if sw=01
CMP | 1000 00sw | mod 111 r/m | data | data if sw=01
2:
OR | 1000 000w | mod reg r/m | data | data if w=1
TEST | 1111 011w | mod 000 r/m | data | data if w=1
MOV | 1100 011w | mod 000 r/m | data | data if w=1
AND | 1000 000w | mod 100 r/m | data | data if w=1
XOR | 1000 000w | mod 110 r/m | data | data if w=1
3:
LDS | 1100 0101 | mod reg r/m
LES | 1100 0100 | mod reg r/m
ADD | 0000 00dw | mod reg r/m
OR | 0000 10dw | mod reg r/m
ADC | 0001 00dw | mod reg r/m
SBB | 0001 10dw | mod reg r/m
SUB | 0010 10dw | mod reg r/m
AND | 0010 00dw | mod reg r/m
CMP | 0011 10dw | mod reg r/m
XOR | 0011 00dw | mod reg r/m
MOV | 1000 10dw | mod reg r/m
LEA | 1000 1101 | mod reg r/m
TEST | 1000 010w | mod reg r/m
XCHG | 1000 011w | mod reg r/m
4:
NEG | 1111 011w | mod 011 r/m
MUL | 1111 011w | mod 100 r/m
IMUL | 1111 011w | mod 101 r/m
NOT | 1111 011w | mod 010 r/m
DIV | 1111 011w | mod 110 r/m
IDIV | 1111 011w | mod 111 r/m
INC | 1111 111w | mod 000 r/m
DEC | 1111 111w | mod 001 r/m
CALL | 1111 1111 | mod 010 r/m
CALL | 1111 1111 | mod 011 r/m
JMP | 1111 1111 | mod 100 r/m
JMP | 1111 1111 | mod 101 r/m
PUSH | 1111 1111 | mod 110 r/m
SHL | 1101 00vw | mod 100 r/m
SHR | 1101 00vw | mod 101 r/m
SAR | 1101 00vw | mod 111 r/m
ROL | 1101 00vw | mod 000 r/m
ROR | 1101 00vw | mod 001 r/m
RCL | 1101 00vw | mod 010 r/m
RCR | 1101 00vw | mod 011 r/m
POP | 1000 1111 | mod 000 r/m
5:
ESC | 1101 1xxx | mod xxx r/m
MOV | 1000 1100 | mod 0sg r/m
MOV | 1000 1110 | mod 0sg r/m
6:
ADD | 0000 010w | data | data if w=1
OR | 0000 110w | data | data if w=1
ADC | 0001 010w | data | data if w=1
SBB | 0001 110w | data | data if w=1
SUB | 0010 110w | data | data if w=1
AND | 0010 010w | data | data if w=1
CMP | 0011 110w | data | data if w=1
XOR | 0011 010w | data | data if w=1
TEST | 1010 100w | data | data if w=1
MOV | 1011 wreg | data | data if w=1
7:
JMP | 1110 1010 | olo | ohi | slo | shi
CALL | 1001 1010 | olo | ohi | slo | shi
8:
MOV | 1010 000w | lo | hi
MOV | 1010 001w | lo | hi
RET | 1100 0010 | lo | hi
RET | 1100 1010 | lo | hi
CALL | 1110 1000 | lo | hi
JMP | 1110 1001 | lo | hi
9:
LOOPNE | 1110 0000 | disp
LOOPE | 1110 0001 | disp
LOOP | 1110 0010 | disp
JCXZ | 1110 0011 | disp
JMP | 1110 1011 | disp
JE | 0111 0100 | disp
JL | 0111 1100 | disp
JLE | 0111 1110 | disp
JB | 0111 0010 | disp
JBE | 0111 0110 | disp
JP | 0111 1010 | disp
JO | 0111 0000 | disp
JS | 0111 1000 | disp
JNE | 0111 0101 | disp
JNL | 0111 1101 | disp
JNLE | 0111 1111 | disp
JNB | 0111 0011 | disp
JNBE | 0111 0111 | disp
JNP | 0111 1011 | disp
JNO | 0111 0001 | disp
JNS | 0111 1001 | disp
10:
IN | 1110 010w | port
OUT | 1110 011w | port
INT | 1100 1101 | type
11:
AAM | 1101 0100 | 0000 1010
AAD | 1101 0101 | 0000 1010
12:
PUSH | 000s g110
POP | 000s g111
13:
INC | 0100 0reg
DEC | 0100 1reg
PUSH | 0101 0reg
POP | 0101 1reg
XCHG | 1001 0reg
14:
MOVS | 1010 010w
CMPS | 1010 011w
SCAS | 1010 111w
LODS | 1010 110w
STOS | 1010 101w
IN | 1110 110w
OUT | 1110 111w
15:
RET | 1100 0011
RET | 1100 1011
INT 3 | 1100 1100
INTO | 1100 1110
IRET | 1100 1111
XLAT | 1101 0111
NOP | 1001 0000
CBW | 1001 1000
CWD | 1001 1001
WAIT | 1001 1011
PUSHF | 1001 1100
POPF | 1001 1101
SAHF | 1001 1110
LAHF | 1001 1111
DAS | 0010 1111
AAA | 0011 0111
BAA | 0010 0111
AAS | 0011 1111
DAS | 0010 1111
AAA | 0011 0111
BAA | 0010 0111
AAS | 0011 1111
HLT | 1111 0100
CMC | 1111 0101
CLC | 1111 1000
STC | 1111 1001
CLI | 1111 1010
STI | 1111 1011
CLD | 1111 1100
STD | 1111 1101
16:
LOCK | 1111 0000
REP | 1111 0010
REPNE | 1111 0011
Асемблер nasm
Ставимо популярний x86 асемблер з підтримкою усіх розширень Intel: MMX, SSE, SSE2, SSE3, SSE4.1 SSE4.2, SSE5, KNI, AVX, AVX-2, AVX-512, AES, AMX, ADX, TSX, MPX, FMA, VNNI, GFNI, SGX, CET, PKU. Ми будемо працювати в x86-64 режимі, з Mach-O вихідним форматом.
$ brew install nasm
Нуль-програма.
$ cat dasm.asm
global _main
default rel
section .text
_main: ret
section .data
Асемблювання, лінковка та запуск.
$ nasm -f macho64 -o dasm.o dasm.asm
gcc -fno-pie -o dasm dasm.o
./dasm
Таблиці
Почнемо практикум. Ми будемо робити діспатч по першому байту інструкції ігноруючи поки префікси, якшо це префікс ми будемо його просто запам`ятовувати у якості змінної в контексті. Будемо робити демонстрацію для перших чотирьох опкодів 00 01 02 03, але покажемо як заповнюється таблиця основного діспатча. Тут число 7 означає кількість послідовних комбінаторів мотузок які викликаються в процесі парса інструкції та друкують символи на термінал. Вже можна зробити покращення виділивши сімки в окрему область вірівняну по границі байту. Але тут для простоти вирівняємо і адресу мікрокоду діспатчу і кількість його елементів по 8-байтовій границі.
opcode: dq inst00, 7, inst01, 7, inst02, 7, inst03, 7
dq inst04, 7, inst05, 7, inst06, 7, inst07, 7
dq inst08, 7, inst09, 7, inst0A, 7, inst0B, 7
dq inst0C, 7, inst0D, 7, inst0E, 7, inst0F, 7
Кожна адреса з таблиці опкодів містить вирівняну по границі слова послідовність кожен елемент якої 16-бітне число, старший (по адресам) байт якого містить параметр, а молодший номер функції-комбінатора в таблиці функцій ropes), який друкує певну частину візуалізації інструкції (назву, операнд, дужки та навіть line feed).
0 — парсер mod_rm байт (преамбула);
1 — друк назви операції з таблиці символьних мнемонік;
2 — друк відкриття дужки та початку адресації;
3 — друк відносної адреси, якщо така використовується;
4 — друк закриття дужки;
5 — друк коми;
6 — друк регістру;
7 — друк A.
Для байткодів 02 та 03 де порядок аргументів звороній, алгоритм побудови зображення інструкції буде видозміненим: (0,1,6,5,2,3,4,7). У випадку коли mod=11 такий алгоритм повинен бути наступним: (0,1,6,5,6,7).
inst0: db 0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0
inst2: db 0,0,1,0,6,0,5,0,2,0,3,0,4,0,7,0
inst1 equ inst0
inst3 equ inst2
Таблиця функцій
Для побудови дизасемблера для mod r/m-байтових інструкцій достатньо наступного набору комбінаторів:
ropes: dq parse_mod_rm
dq print_add
dq print_rm
dq print_hex
dq brace
dq comma
dq print_reg
dq eol
У якості прикладу розмістимо в пам'яті секції .data наступнs інструкції. Для простоти ми неправильно оброблямо mod r/m байт, та трактуємо поле mod без виключення коли mod=11, тобто трактуємо це як відностну трьохбайтову адресу (4 та 6 інструкції).
machine: db 0x00, 0b00100101
db 0x01, 0b10010011, 0x26, 0x25
db 0x02, 0b01001110, 0x34
db 0x03, 0b11101110, 0x76, 0x75, 0x77
db 0x02, 0b10111111, 0x66, 0x65
db 0x03, 0b11011101, 0x86, 0x85, 0x88
machine.ptr: dq machine
Допоміжні символьні таблиці
rm000: db '[BX+SI'
rm001: db '[BX+DI'
rm010: db '[BP+SI'
rm011: db '[BP+DI'
rm100: db '[SI'
rm101: db '[DI'
rm110: db '[BP'
rm111: db '[BX'
rmter: db ']'
com: db ','
linefeed: db 0xa
rm: dq rm000, rm001, rm010, rm011, rm100, rm101, rm101, rm110, rm111
hexout: db '+0x01234567890123456789012345678901'
regb: db "ALCLDLBLAHCHDHBH"
regw: db "AXCXDXBXSPBPSIDI"
hex: db '01234567890ABCDEF'
Синоніми для syscall операційної системи MacOS.
syscall_exit equ 0x2000001
syscall_write equ 0x2000004
syscall_open equ 0x2000005
syscall_close equ 0x2000006
Передача параметрів
Основні вимоги до функції: мінімізація використання операцій зі стеком, використовувати передачу параметрів через регістри, мінімізація звертань до шини, бампінг короткими версіями інструкцій (бажано мінімальною довжиною 2 байти), функції-мотузочки (ropes) довжиною у розмір конвеєра.
Бібліотека функцій
Rope #0. Функція обробки байту MOD-R/M
Перед початком виконання після головного діспатчу ми попадаємо в парсер mod r/m байта, де ми завантажуємо адресу поточної інструкції в rsi (джерело для дизасемблера) та адресу таблиці опкодів в rdi (ціль дизасемблювання). У цій функції ми заглядаємо у попередній байт [rsi-1] чим вносимо цей парсер до класу L(1). Попередній байт (опкод інструкці) ми зберігаємо в регістрі BL, а байт r/m в регістрі AL. Використовуючи зсув BL на 4 ми визначаємо позицію а таблиці опкодів та використовуємо її у відносній адресації [rdi+rbx] щоб визначити адресу фукнції-мотузки у яку небхідно модифікувати параметри для розпізнання на наступних кроках. В даному випадку йдеться про дві функції мотузки from_reg і to_reg, які модифікують аргументи в таблицях inst0 та inst1 або таблицях inst2 та inst3. Ці функції записують в байт-аргумент фукнці-мотузки наступні параметри: 1) для фукнкції регістру — номер регістру з байту mod r/m у молодих 4-бітах AL та розрядність інструкції у 5-му біті AL (береться з байткоду), 2) для r/m відображення — значення mod з байту яке індексує вигляд адресації у масиві rm, 3) для функції відображення відносної адреси — кількість байт які небходно прочитати з потоку інструкцій (значення поля mod). Тому кожна з фунції 7 та 8 пише в кожні з цих трох місць, по суті готує майбутні параметри для лінійного проходження. Після виконання функція збільшує адресу поточної інструкції.
parse_mod_rm: mov rsi, [machine.ptr]
mov rdi, opcodes
mov al, [rsi]
mov ah, [rsi-1]
mov bl, ah
mov cl, 4
shl ah, cl
shl bl, cl
mov rdi, [rdi+rbx]
dec cl
mov bl, al
mov dl, al
shr bl, cl
add cl, cl
shr al, cl
inc cl
and bl, cl
and dl, cl
or bl, ah
inc cl
add cl, cl
mem: test bl, cl
jnz odd
call from_reg
jmp quit_rm
odd: call to_reg
quit_rm: inc qword [machine.ptr]
mov rax, 2
ret
Rope #1. Функція друку операції
print_add:
mov rsi, add
mov dl, 4
call write
mov rax, 2
ret
Rope #2. Функція друку адресації памʼяті
print_rm: and rax, 255
mov rdi, rax
shl rdi, 3
mov rbx, rm
mov rsi, [rbx+rdi]
mov rbx, rd
mov dl, [rbx+rdi]
call write
mov rax, 2
ret
Rope #3. Функція друку шістнадцяткового числа
print_hex: push rax
cmp rax, 0
jz empty
xor edx, edx
mov edx, eax
mov rdi, hex
mov rsi, [machine.ptr]
mov rbp, hexout
mov eax, edx
shl eax, 1
add al, 3
mov qword [hexout.len], rax
dec eax
add rbp, rax
pair: mov bl, [rsi]
and ebx, 15
mov al, [rbx+rdi]
mov [rbp], al
dec rbp
mov bl, [rsi]
shr ebx, 4
and ebx, 15
mov al, [rbx+rdi]
mov [rbp], al
dec rbp
inc rsi
dec edx
jne pair
mov rdx, qword [hexout.len]
mov rax, syscall_write
mov rsi, hexout
mov rdi, 1
syscall
empty: pop rdx
add qword [machine.ptr], rdx
mov rax, 2
ret
Rope #4. Функція друку дужки
brace: mov rsi, rmter
mov dl, 1
call write
mov rax, 2
ret
Rope #5. Функція друку коми
comma: mov rsi, com
mov dl, 1
call write
mov rax, 2
ret
Rope #6. Функція друку регістра
print_reg: test al, 16
jnz w
mov rsi, regb
jmp e
w: mov rsi, regw
e: and al, 15
shl al, 1
add rsi, rax
mov dl, 2
call write
mov rax, 2
ret
Rope #7. Функція-патч для регістрових вихідних операндів
from_reg: mov [rdi+7], al ; mod
mov [rdi+13], bl ; reg
mov [rdi+5], dl ; r/m
ret
Rope #8. Функція-патч для регістрових вхідних операндів
to_reg: mov [rdi+11], al ; mod
mov [rdi+5], bl ; reg
mov [rdi+9], dl ; r/m
ret
Головна програма
У головній програмі ми вимушені скористатися стеком для приховування регістра rdx та rdi у процесі виконання функцій мотузок. Саме в цій функції ми передаємо однобайтові паратери функціям через регістр AL, а регістр rsi в циклі завжди виставляється заново в адресу мотузок для відносної адресації по [rsi+rbx], де rbx=8*i, де i — номер функції мотузки в таблиці функцій. Після виходу з функції мотузки ми беремо то що вернулося в rax (використовуємо як вихідний результат функції), та збільшуємо на це число адресу наступної функції мотузки rdi (тобто використовуємо результат для обчислення адреси де знаходиться адреса наступної функції-мотузки). Регістр rdx використовуємо як лічильник циклу (в даному випадку ми крутимо число 7), та кожен раз декриментуємо його. Адресу display.ptr використовуємо як лінільник дизасембльованих інструкцій.
_main: mov rsi, [machine.ptr]
xor rbx, rbx
inc qword [machine.ptr]
mov bl, [rsi]
shl rbx, 4
mov rsi, opcodes
mov rdx, [rsi+rbx+8]
mov rdi, [rsi+rbx]
line: push rdi
push rdx
xor rbx, rbx
mov bl, [rdi]
shl bl, 3
mov rsi, ropes
mov al, [rdi+1]
call [rsi+rbx]
pop rdx
pop rdi
add rdi, rax
shr rax, 1
sub rdx, rax
jnz line
call eol
dec qword [display.ptr]
jnz _main
mov rax, 0x2000001
xor rdi, rdi
syscall
ret
Виконання
nasm -f macho64 -o dasm.o dasm.asm
gcc -fno-pie -o dasm dasm.o
be dasm
[:0x3d2f] [d]

$ ./dasm
ADD [DI],AH
ADD DX,[BP+DI+0x2526]
ADD [DI+0x34],CL
ADD BP,[DI+0x777576]
ADD [BP+0x6566],BH
ADD BX,[DI+0x888586]
DYI PC XT
На курсі "КВ-123-16 Схемотехніка" вивчається як будувати XT комп`ютери та збирати їх самому, на прикладі платформи PC/104. Тут показана повна елементна база для побудови ХТ комп`ютера для максимального та мінімального режимів.
№ | Чіп | Опис |
---|---|---|
1 | 2142 | 1024 x 4-bit SRAM |
2 | 2764 | 8K EPROM |
3 | 6264 | 8K SRAM |
4 | 8086 | 16-bit bus CPU |
5 | 8087 | 16-bit bus MPU |
6 | 8088 | 8-bit bus CPU |
7 | 8089 | 16-bit bus MPU |
8 | 8155 | 256 x 8-bit SRAM, I/O, Timer |
9 | 8212 | 8-bit I/O port |
A | 8214 | PICU |
A | 8254 | PIC 8086 |
B | 8257 | DMA |
C | 8282 | 8-bit latch |
D | 8284 | Clock generator |
E | 8286 | Transeiver |
F | 8288 | Bus controller |
G | 8289 | Bus arbiter |
H | 74LS244 | Octal buffer |
I | 74LS245 | Bi-directional octal buffer |
J | 74LS373 | 8-bit latch |
Приклад компоновки процесору 8088 у мінімальному режимі

Код
Репозиторій проекту github.com/5HT/xt-dasm.
Рекомендована література
[1]. Rector, Alexy. 8086 Book. Berkley. 1980.
[2]. Brey. The Intel Microprocessors. 2009.
[3]. Mathur. 8086: Architecture, Programming and Interfacing. Delhi. 2011.
[4]. Intel Mnemonics. 1980.
Альтернативний практикум на асемблері
Розробка дизасемблера ARM або RISC-V на вибір.
Розробка LISP інтерпретатора.
Наступні кроки
Імітаційне моделювання (розробка емулятора) та математичне моделювання (доведення коректності бінарного коду мікропроцесора). Нас цікавитимуть такі моделі, які враховують мультиагентний доступ до шини та операції LOCK.